package com.ainge.easystudy.designpattern.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

/**
 * JDK动态代理
 * 1.需要实现InvocationHandler接口
 *
 * @author: Ainge
 * @Time: 2021/4/24 16:07
 */
public class JDKProxy implements InvocationHandler {

    /**
     * 目标对象
     */
    private Object target;

    /**
     * 获取目标对象的代理对象
     *
     * 由于代理对象Object它与target对象有共同的接口。
     * 所以需要将代理对象Object向上强转为具体的接口，才能调用方法
     * @param target 目标对象
     * @return 被代理的对象（会实现目标对象的所有接口，所有不在接口内的方面，目标对象独有的，是无法被代理调用的）
     */
    public Object getJDKProxyInstance(Object target){
        Objects.requireNonNull(target);
        // 字节码重组原理如下：
        // 2.获取被代理对象的引用
        this.target = target;
        Class<?> aClass = target.getClass();
        // 3.反射获取被代理对象的所有接口
        // 4.JDK动态代理类会重新生成一个新的类，同时新的类要实现被代理类实现的所有接口
        // 5.动态生成JAVA代码，新增的业务方法由一定的逻辑代码调用(在代码中体现)
        // 6.编译新生成的JAVA代码.class文件 重新加载到JVM中
        return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 代理proxy对象会持有this的引用
        // 环绕增强
        long currentTime = before();
        Object invoke = method.invoke(this.target, args);
        after(currentTime);
        return invoke;
    }

    private long before(){
        System.out.println("方法执行开始计时...");
        return System.currentTimeMillis();
    }
    private void after(long currentTime){
        System.out.println("方法执行结束耗时：" + (System.currentTimeMillis() - currentTime) + " ms");
    }

}
